package robocup.connection;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * The RCSSConnection handles a bidirectional connection to the soccer server.
 * It uses UDP as protocol, and offers public send- and receive-methods.
 * 
 */
public class RCSSConnection {

  /**
   * DatagramSocket, which listens on a defined port for incoming UDP-Packets.
   */
  private DatagramSocket socket        = null;

  /**
   * DatagramPacket in which data is wrapped into and sent to the server.
   */
  private DatagramPacket sendPacket    = null;

  /**
   * DatagramPacket, in which incoming data is wrote into.
   */
  private DatagramPacket recvPacket    = null;

  /**
   * The InetAddress of the server.
   */
  private InetAddress    host          = null;

  /**
   * The port of the server.
   */
  private int            port          = 6000;

  /**
   * The host-name of the server.
   */
  private String         hostname      = "localhost";

  /**
   * The buffer, in which raw data from a received message is wrote into. This
   * can be returned as string.
   */
  private final ByteBuffer     receiveBuffer = new ByteBuffer(4000);

  /**
   * The buffer, in which a string for a sending message is wrote into. This can
   * be returned as raw data for sending.
   */
  private final ByteBuffer     sendBuffer    = new ByteBuffer(4000);

  /**
   * Flag, indicating, if a port was given.
   */
  private boolean        no_port       = true;

  /**
   * The timeout for receiving messages (in milliseconds). Note that this will
   * cause the agent to terminate, if for this duration no message is received,
   * assuming that the server is dead.
   */
  private final int            timeout       = 3000;

  /**
   * The latest command, that should be sent to the server.
   */
  private String         command       = null;

  /**
   * <code>RCSSConnection</code> constructor This constructor initializes all
   * necessary member variables for the connection to the soccer server.
   * 
   * @param port -
   *          the server port
   * @param hostname -
   *          the server host
   */
  public RCSSConnection(
      final String hostname,
      final int port) {

    this.hostname = hostname;
    this.port = port;
    try {
      this.host = InetAddress.getByName(this.hostname);
      this.recvPacket = new DatagramPacket(this.receiveBuffer.getByteArray(),
          this.receiveBuffer.length());
      this.sendPacket = new DatagramPacket(this.sendBuffer.getByteArray(),
          this.sendBuffer.length(), this.host, port);
      this.socket = new DatagramSocket();
      this.socket.setSoTimeout(this.timeout);
      this.socket.setReceiveBufferSize(10*this.socket.getReceiveBufferSize());
    }
    catch (final Exception e) {
      e.printStackTrace();
    }

  }

  /**
   * Method for sending data to the rcssserver
   * 
   * @param message -
   *          the message
   * @throws IOException -
   *           occurs on unexpected io-errors
   */
  public void send(final String message) throws IOException {

    this.sendBuffer.reset();
    this.sendBuffer.setString(message);
    this.sendPacket.setData(this.sendBuffer.getByteArray());
    this.sendPacket.setLength(this.sendBuffer.length());
    this.sendPacket.setAddress(this.host);
    this.sendPacket.setPort(this.port);
    this.socket.send(this.sendPacket);
    // debug only
    this.command = message;
  }

  /**
   * This method receives DataGramms form from the rcssserver, and returns the
   * contained data as string. Note that this method blocks the calling thread
   * and will return <code>null</code>, if a timeout occurs. This will
   * shutdown the agent.
   * 
   * @return - the received string
   */
  public String receive() {

    try {
      this.receiveBuffer.reset();
      this.recvPacket.setData(this.receiveBuffer.getByteArray());
      this.recvPacket.setLength(this.receiveBuffer.getByteArray().length);
      this.socket.receive(this.recvPacket);

      // once check host and port
      if (this.no_port) {
        this.host = this.recvPacket.getAddress();
        this.port = this.recvPacket.getPort();
        this.no_port = false;
      }
      final String str = this.receiveBuffer.getString();
      return str;
    }
    catch (final IOException e) {
      return null;
    }
  }

  /**
   * This method return the last sent command
   * 
   * @return a string containing the last command!
   */
  public String getCommand() {

    return this.command;
  }

}
